package src.uipackage.ui {
   
   import flash.geom.ColorTransform;
   import flash.geom.Transform;
   import flash.geom.Rectangle;
   import flash.display.Sprite;
   import flash.display.DisplayObjectContainer;
   import flash.events.MouseEvent;
   import flash.events.Event;
   import flash.filters.DropShadowFilter;

   import com.onebyonedesign.ui.events.ScrollBarEvent;
   
   	/**
	* UI widget that creates a "full screen" scrollbar to scroll Sprite or MovieClip content.
	* @author Devon O.
	*/
   public class OBO_FullScrollBar extends Sprite {
      private static var OBO_FSB:OBO_FullScrollBar;
      
      public static const RIGHT:String = "right";
      public static const LEFT:String = "left";
      
      private var _addedToStage:Boolean = false;
      private var _pressed:Boolean = false;
      
      private var _content:DisplayObjectContainer;
      private var _scrollValue:Number;
      private var _easeAmount:int;
      private var _scrollBar:Sprite;
      private var _scroller:Sprite;
      private var _scrollTrack:Sprite;
      private var _trackColor:uint;
      private var _scrollerColor:uint;
      private var _position:String;
      private var _useScrollWheel:Boolean;
   
      	/**
		* singleton class - use static createScrollBar() method for instantiation.
		* @private
		*/

      function OBO_FullScrollBar(creator:Function, tc:uint, sc:uint, c:DisplayObjectContainer, e:int = 1, pos:String = RIGHT, sw:Boolean = false) {
		 if (!(creator == makeInstance)) throw new Error("OBO_FullScrollBar error: class must be instantiated with static OBO_FullScrollBar.createScrollBar() method.");
         if (pos != "right" && pos != "left") throw new Error("OBO_FullScrollBar error: constructor method position argument requires either \"left\" (OBO_FullScrollBar.LEFT) or \"right\" (OBO_FullScrollBar.RIGHT).");
         
         _trackColor = tc;
         _scrollerColor = sc;
         _content = c;
         _easeAmount = (e > 0) ? Math.ceil(e) : 1;
         _position = pos;
         _useScrollWheel = sw;
         
         initScroller();
      }
      
      private function initScroller():void {
         makeScrollBar();
         addChild(_scrollBar);
         
         _scrollBar.addEventListener(Event.ADDED_TO_STAGE, addedListener);
         
         _scroller.buttonMode = true;
         _scroller.useHandCursor = false;
      }
      
      private function addedListener(e:Event):void {
         _scrollBar.removeEventListener(Event.ADDED_TO_STAGE, adjustSize);
         _addedToStage = true;
         
         if (_useScrollWheel) stage.addEventListener(MouseEvent.MOUSE_WHEEL, mouseWheelListener);
         _scroller.addEventListener(MouseEvent.MOUSE_DOWN, onDownListener);
         stage.addEventListener(MouseEvent.MOUSE_UP, onUpListener);
         
         adjustSize();
      }
      
      private function onDownListener(me:MouseEvent):void {
         _pressed = true;
         dispatchEvent(new ScrollBarEvent(ScrollBarEvent.SCROLL_DOWN));
         
         _scroller.startDrag(false, new Rectangle(0, 0, 0, stage.stageHeight - _scroller.height));
         stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveListener);
      }
      
      private function onUpListener(me:MouseEvent):void {
         if (_pressed) {
            _pressed = false;
            dispatchEvent(new ScrollBarEvent(ScrollBarEvent.SCROLL_UP));
            
            _scroller.stopDrag();
            stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveListener);
         }
      }
      
      private function mouseWheelListener(me:MouseEvent):void {
         var d:Number = me.delta;
         if (d > 0) {
            if ((_scroller.y - (d * 4)) >= 0){
                  _scroller.y -= d * 4;
               } else {
                  _scroller.y = 0;
               }
               if (!_scroller.willTrigger(Event.ENTER_FRAME)) _scroller.addEventListener(Event.ENTER_FRAME, scrollContent);
            } else {
               if (((_scroller.y + _scroller.height) + (Math.abs(d) * 4)) <= stage.stageHeight){
                  _scroller.y += Math.abs(d) * 4;
               } else {
                  _scroller.y = stage.stageHeight - _scroller.height;
               }
               if (!_scroller.willTrigger(Event.ENTER_FRAME)) _scroller.addEventListener(Event.ENTER_FRAME, scrollContent);
            }
      }
      
      private function mouseMoveListener(me:MouseEvent):void {
         me.updateAfterEvent();
         if (!_scroller.willTrigger(Event.ENTER_FRAME)) _scroller.addEventListener(Event.ENTER_FRAME, scrollContent);
      }
      
      private function scrollContent(e:Event):void {
         var ty:Number = -((_content.height - stage.stageHeight) * (_scroller.y / _scrollValue));
         var dist:Number = ty - _content.y;
         var moveAmount:Number = dist / _easeAmount;
         _content.y += moveAmount;
         
         if (Math.abs(_content.y - ty) < .5) {
            _scroller.removeEventListener(Event.ENTER_FRAME, scrollContent);
            _content.y = Math.round(ty);
         }
      }
      
      
      private function makeScrollBar():void {
         _scrollBar = new Sprite();
         _scrollTrack = createTrack();
         _scroller = createScroller();
         _scrollBar.addChild(_scrollTrack);
         _scrollBar.addChild(_scroller);
      }
      
      private function createTrack():Sprite {
         var t:Sprite = new Sprite();
         
         t.graphics.beginFill(_trackColor);
         t.graphics.drawRect(0, 0, 16, 16);
         t.graphics.endFill();
      
         return t;
      }
      
      private function createScroller():Sprite {
         var s:Sprite = new Sprite();

         s.graphics.beginFill(_scrollerColor);
         s.graphics.drawRect(0, 0, 16, 16);
         s.graphics.endFill();
		 s.filters = [ new DropShadowFilter(1, 90, 0x000000, .6, 3, 3) ]

         return s;
      } 
      
      /**
       * The OBO_FullScrollBar creates a scrollbar the full size of the Stage and may be used to scroll MovieClip or Sprite instances which, themselves, may be entire websites or applications.
	   * The OBO_FullScrollBar class is a Singleton and needs to be instantiated with the static createScrollBar() method.
       * @param	trackColor		Hexadecimal color of scrollbar track
       * @param	scrollerColor	Hexadecimal color of scrollbar dragger/scroller
       * @param	content			The MovieClip or Sprite instance which will be scrolled.
       * @param	easeAmount		The amount of easing. 1 equals no ease. The higher the number the more easing.
       * @param	position		Either OBO_FullScrollBar.LEFT or OBO_FullScrollBar.RIGHT. Determines on which side of the screen the scrollbar will appear.
       * @param	useMouseWheel	True if scrollbar should be scrolled with mousewheel. False otherwise.
	   * 
       */ 
      public static function createScrollBar(trackColor:uint, scrollerColor:uint, content:DisplayObjectContainer, easeAmount:int = 1, position:String = RIGHT, useMouseWheel:Boolean = false):OBO_FullScrollBar {
         if (OBO_FSB == null) OBO_FSB = OBO_FullScrollBar.makeInstance(trackColor, scrollerColor, content, easeAmount, position, useMouseWheel);
         return OBO_FSB;
      }
	  
	  private static function makeInstance(trackColor:uint, scrollerColor:uint, content:DisplayObjectContainer, easeAmount:int, position:String, useMouseWheel:Boolean):OBO_FullScrollBar {
			return new OBO_FullScrollBar(arguments.callee, trackColor, scrollerColor, content, easeAmount, position, useMouseWheel);
	  }
      
      
      /**
       * Adusts and repositions scrollbar. Call this whenever the Stage or content object is resized.
       */ 
      public function adjustSize():void {
         if (_addedToStage) {
            _scrollBar.x = (_position == LEFT) ? 0 : stage.stageWidth - 16;
            _scrollTrack.height = stage.stageHeight;
            _scroller.height = Math.ceil((stage.stageHeight / _content.height) * stage.stageHeight);
            if ((_scroller.y + _scroller.height) > stage.stageHeight) _scroller.y = stage.stageHeight - _scroller.height;
            _scroller.y = (_content.height < stage.stageHeight) ? 0 : _scroller.y;
            _scrollBar.visible = (_content.height > stage.stageHeight);
            _scrollValue = stage.stageHeight - _scroller.height;
            _content.y = -((_content.height - stage.stageHeight) * (_scroller.y / _scrollValue));
         }
      }
      
      /**
       * Allows or disallows mouse wheel scrolling.
       */ 
      public function set useScrollWheel(b:Boolean):void {
         if (b && _addedToStage && !stage.willTrigger(MouseEvent.MOUSE_WHEEL)) {
            stage.addEventListener(MouseEvent.MOUSE_WHEEL, mouseWheelListener);
         } else if (stage.willTrigger(MouseEvent.MOUSE_WHEEL)) {
            stage.removeEventListener(MouseEvent.MOUSE_WHEEL, mouseWheelListener);
         }
      }
      
	  /**
	   * Returns the boolean value of the current mousewheel usage.
	   */
      public function get useScrollWheel():Boolean {
         return _useScrollWheel;
      }
      
      /**
       * Sets the display object (MovieClip or Sprite) which will be scrolled
       */ 
      public function set scrollTarget(targ:DisplayObjectContainer):void {
         _content = targ;
         adjustSize();
      }
      
	  /**
	   * Returns a reference to the current scroll target.
	   */
      public function get scrollTarget():DisplayObjectContainer {
         return _content;
      }
      
      /**
       * Sets the amount of ease to use.
       */
      public function set easeAmount(e:int):void {
         _easeAmount = (e <= 1 ) ? 1 : e;
      }
      
	  /**
	   * Returns the current amount of ease.
	   */
      public function get easeAmount():int {
         return _easeAmount;
      }
      
      /**
       * Sets the color of the scrollbar track.
       */
      public function set trackColor(col:uint):void {
         var tColor_cxf:ColorTransform = new ColorTransform();
         _trackColor = col;
         tColor_cxf.color = _trackColor;
         _scrollTrack.transform.colorTransform = tColor_cxf;
      }
      
	  /**
	   * Returns the color of the scrollbar track.
	   */
      public function get trackColor():uint {
         return _trackColor;
      }
      
      /**
       * Sets the color of the scrollbar scroller.
       */
	  
      public function set scrollerColor(col:uint):void {
         var sColor_cxf:ColorTransform = new ColorTransform();
         _scrollerColor = col;
         sColor_cxf.color = _scrollerColor;
         _scroller.transform.colorTransform = sColor_cxf;
      }
      
	  /**
	   * Returns the color of the scrollbar scroller.
	   */
      public function get scrollerColor():uint {
         return _scrollerColor;
      }
   }
}
